/*
 * Copyright (c) 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */

package com.facebook.common.util;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

import com.facebook.common.internal.ByteStreams;
import com.facebook.common.internal.Preconditions;

/**
 * Utility method for dealing with Streams.
 */
public class StreamUtil {

    /**
     * Efficiently fetch bytes from InputStream is by delegating to
     * getBytesFromStream(is, is.available())
     */
    public static byte[] getBytesFromStream(final InputStream is) throws IOException {
        return getBytesFromStream(is, is.available());
    }

    /**
     * Efficiently fetch the bytes from the InputStream, provided that caller can guess
     * exact numbers of bytes that can be read from inputStream. Avoids one extra byte[] allocation
     * that ByteStreams.toByteArray() performs.
     *
     * @param hint - size of inputStream's content in bytes
     */
    public static byte[] getBytesFromStream(InputStream inputStream, int hint) throws IOException {
        // Subclass ByteArrayOutputStream to avoid an extra byte[] allocation and copy
        ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(hint) {
            @Override
            public byte[] toByteArray() {
                // Can only use the raw buffer directly if the size is equal to the array we have.
                // Otherwise we have no choice but to copy.
                if (count == buf.length) {
                    return buf;
                } else {
                    return super.toByteArray();
                }
            }
        };
        ByteStreams.copy(inputStream, byteOutput);
        return byteOutput.toByteArray();
    }

    /**
     * Skips exactly bytesCount bytes in inputStream unless end of stream is reached first.
     *
     * @param inputStream input stream to skip bytes from
     * @param bytesCount  number of bytes to skip
     * @return number of skipped bytes
     * @throws java.io.IOException
     */
    public static long skip(final InputStream inputStream, final long bytesCount) throws IOException {
        Preconditions.checkNotNull(inputStream);
        Preconditions.checkArgument(bytesCount >= 0);

        long toSkip = bytesCount;
        while (toSkip > 0) {
            final long skipped = inputStream.skip(toSkip);
            if (skipped > 0) {
                toSkip -= skipped;
                continue;
            }

            if (inputStream.read() != -1) {
                toSkip--;
                continue;
            }
            return bytesCount - toSkip;
        }

        return bytesCount;
    }
}
